<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../../resources/gesture-util.js"></script>
<style>
div {
  position: absolute;
}
#scroller {
  width: 500px;
  height: 500px;
  overflow: scroll;
  scroll-snap-type: both mandatory;
  border: solid black 5px;
}
#space {
  width: 2000px;
  height: 2000px;
}
.target {
  width: 200px;
  height: 200px;
  scroll-snap-align: start;
  background-color: blue;
}
</style>

<div id="scroller">
  <div id="space"></div>
  <div class="target" style="left: 0px; top: 0px;"></div>
  <div class="target" style="left: 80px; top: 80px;"></div>
  <div class="target" style="left: 200px; top: 200px;"></div>
</div>

<script>
if (window.internals) {
  internals.runtimeFlags.implicitRootScrollerEnabled = true;
  // Jump directly to the snap position.
  internals.settings.setScrollAnimatorEnabled(false);
}

var body = document.body;
var scroller = document.getElementById("scroller");
var space = document.getElementById("space");

function scrollLeft() {
  return scroller.scrollLeft;
}

function scrollTop() {
  return scroller.scrollTop;
}

function approx_equal(callback, value, tolerance) {
  return Math.abs(callback() - value) < tolerance;
}

// MacOS devices do not have touch screens. Tests are irrelevant.
const macPlatform = navigator.platform.indexOf('Mac') == 0;

promise_test (async () => {
  if (macPlatform)
    return;

  scroller.scrollTo(0, 0);
  const scrollPromise = waitForScrollEvent(scroller);
  await smoothScroll(100, 200, 200, GestureSourceType.TOUCH_INPUT, 'downright', SPEED_INSTANT);
  await scrollPromise;
  // Use time-based detection of the scroll end since scrolling past snap point.
  await waitForAnimationEndTimeBased(scrollLeft);
  assert_equals(scrollLeft(), 80);
  assert_equals(scrollTop(), 80);

}, "Without fling enabled, the scroll ends at the closest snap point to the " +
   "scroll destination.");

promise_test (async () => {
  scroller.scrollTo(0, 0);
  const scrollPromise = waitForScrollEvent(scroller);
  await swipe(100, 200, 200, 'upleft', 900);
  await scrollPromise;
  await waitForAnimationEndTimeBased(scrollLeft);
  assert_equals(scrollLeft(), 200);
  assert_equals(scrollTop(), 200);
}, "Fling scroll ends at the closest snap point to the fling destination.");

promise_test (async () => {
  scroller.scrollTo(0, 0);
  const scrollPromise = waitForScrollEvent(scroller);
  // Scroll horizontally to (100, 0) with momentum.
  await swipe(100, 200, 200, 'left', 900);
  await scrollPromise;
  await waitForAnimationEndTimeBased(scrollLeft);
  assert_equals(scrollLeft(), 200);
  assert_equals(scrollTop(), 0);
}, "Fling scroll in one direction ends at closest snap point to the fling " +
   "destination.");

promise_test (async () => {
  scroller.scrollTo(0, 0);
  const scrollPromise = waitForScrollEvent(scroller);
  // Scroll right and up. Going up vertically is not possible since it is
  // outside the bound. The expectation in this case is to snap as if user
  // scrolled horizontally to (100, 0) with momentum.
  await swipe(100, 200, 200, 'downleft', 900);
  await scrollPromise;
  await waitForAnimationEndTimeBased(scrollLeft);
  // TODO(823998): Scroll top may land either at 0px or 80px which are both
  // valid snap targets. My expectation was that it would be at 0. Needs more
  // investigation to deflake.
  // assert_approx_equals(scrollTop(), 0, 1);
}, "Fling scroll that goes outside the boundary ends at the closest snap " +
   "point to the fling destination.");
</script>
